package com.hospital.controller;

import com.alipay.api.AlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hospital.configure.AliPayConfig;
import com.hospital.entity.*;
import com.hospital.mapper.*;
import com.hospital.utils.IDGenerator;
import com.hospital.utils.JsonUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

/**
 * 支付Controller
 */
@Controller
@Slf4j
public class PayController {
    @Autowired
    private AliPayConfig aliPayConfig;
    @Autowired
    private AlipayClient alipayClient;

    @Resource
    private RegisteredMapper registeredMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private FeeRecordMapper feeRecordMapper;

    @Resource
    CaseInfoMapper caseInfoMapper;

    @Resource
    CaseMedicineMapper caseMedicineMapper;

    @Resource
    MedicineMapper medicineMapper;

    /**
     * 支付宝支付
     * 该方法无返回值，执行成功后response回写结果即可
     * 俩接口自己实现。notifyUrl和returnUrl。实现方法也分别在下面那俩。
     */
    @PostMapping("/alipay/payment")
    public void payment(String orderId, String subject, String body, float money, HttpServletResponse response) {
        // 金额保留两位
        money = (float) (Math.round(money * 100)) / 100;
        // 1、设置请求参数
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        // 页面跳转同步通知页面路径
        alipayRequest.setReturnUrl(aliPayConfig.getReturnUrl());
        // 服务器异步通知页面路径
        alipayRequest.setNotifyUrl(aliPayConfig.getNotifyUrl());

        // 2、SDK已经封装掉了公共参数，这里只需要传入业务参数，请求参数查阅开头Wiki
        //根据支付宝的规定，我得对支付宝，发起一个请求，按照他提供的参数（out_trade_no 这些）格式，用它提供的请求工具进行请求
        Map<String, String> map = new HashMap<>(16);
        map.put("out_trade_no", orderId);
        map.put("total_amount", String.valueOf(money));
        map.put("subject", subject);
        map.put("body", body);
        // 销售产品码
        map.put("product_code", "FAST_INSTANT_TRADE_PAY");

        alipayRequest.setBizContent(JsonUtils.objectToJson(map));

        response.setContentType("text/html;charset=utf-8");
        try {
            // 3、生成支付表单。alipayRequest进行请求
            AlipayTradePagePayResponse alipayResponse = alipayClient.pageExecute(alipayRequest);
            if (alipayResponse.isSuccess()) {
                String result = alipayResponse.getBody();
                response.getWriter().write(result);
            } else {
                log.error("【支付表单生成】失败，错误信息：{}", alipayResponse.getSubMsg());
                response.getWriter().write("error");
            }
        } catch (Exception e) {
            log.error("【支付表单生成】异常，异常信息：{}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 该方式仅仅在买家付款完成以后进行自动跳转，因此只会进行一次
     * 支付宝服务器同步通知页面，获取支付宝GET过来反馈信息
     * 该方法执行完毕后跳转到成功页即可
     * （1）该方式不是支付宝主动去调用商户页面，而是支付宝的程序利用页面自动跳转的函数，使用户的当前页面自动跳转；
     * （2）返回URL只有一分钟的有效期，超过一分钟该链接地址会失效，验证则会失败
     * （3）可在本机而不是只能在服务器上进行调试
     */
    @GetMapping("/alipay/return")
    public void alipayReturn(HttpServletRequest request, HttpServletResponse response) {
        // 获取参数
        Map<String, String> params = getPayParams(request);
        try {
            // 验证订单
//            boolean flag = orderInfoService.validOrder(params);
            if (params.get("out_trade_no") != null) {
                // 验证成功后，修改订单状态为已支付
                String orderId = params.get("out_trade_no");
                /*
                 * 订单状态（与官方统一）
                 * WAIT_BUYER_PAY：交易创建，等待买家付款；
                 * TRADE_CLOSED：未付款交易超时关闭，或支付完成后全额退款；
                 * TRADE_SUCCESS：交易支付成功；
                 * TRADE_FINISHED：交易结束，不可退款
                 */
                // 获取支付宝订单号
                String tradeNo = params.containsKey("trade_no") ? params.get("trade_no") : "";

                response.setContentType("text/html;charset=utf-8");
                //在这里写判断到底返回到哪里。是挂号那里还是交药费那里。根据订单编号前面那个进行判断。
                String returnUrl = orderId.indexOf("REGI") > -1 ? "http://localhost:8080/#/Registered" : "http://localhost:8080/#/CaseInfo";

                response.sendRedirect(returnUrl);
            } else {
                log.error("【支付宝同步方法】验证失败");
                response.getWriter().write("支付验证失败");
            }
        } catch (Exception e) {
            log.error("【支付宝同步方法】异常，异常信息：{}", e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 服务器异步通知，获取支付宝POST过来反馈信息
     * 该方法无返回值，静默处理
     * 订单的状态已该方法为主，其他的状态修改方法为辅 *
     * （1）程序执行完后必须打印输出“success”（不包含引号）。
     * 如果商户反馈给支付宝的字符不是success这7个字符，支付宝服务器会不断重发通知，直到超过24小时22分钟。
     * （2）程序执行完成后，该页面不能执行页面跳转。
     * 如果执行页面跳转，支付宝会收不到success字符，会被支付宝服务器判定为该页面程序运行出现异常，而重发处理结果通知
     * （3）cookies、session等在此页面会失效，即无法获取这些数据
     * （4）该方式的调试与运行必须在服务器上，即互联网上能访问 *
     *
     *
     */
    @PostMapping("/alipay/notify")
    public void alipayNotify(HttpServletRequest request, HttpServletResponse response) {
        /*
         默认只有TRADE_SUCCESS会触发通知，如果需要开通其他通知，请联系客服申请
         触发条件名 	    触发条件描述 	触发条件默认值
        TRADE_FINISHED 	交易完成 	false（不触发通知）
        TRADE_SUCCESS 	支付成功 	true（触发通知）
        WAIT_BUYER_PAY 	交易创建 	false（不触发通知）
        TRADE_CLOSED 	交易关闭 	false（不触发通知）
        来源：https://docs.open.alipay.com/270/105902/#s2
         */
        // 获取参数
        Map<String, String> params = getPayParams(request);


        try {
            // 验证订单
            boolean flag = true;
            if (flag) {
                //商户订单号
                String orderId = params.containsKey("out_trade_no") ? params.get("out_trade_no") : "";
                //支付宝交易号
                String tradeNo = params.containsKey("trade_no") ? params.get("trade_no") : "";
                //交易金额
                String totalAmount = params.containsKey("total_amount") ? params.get("total_amount") : "";
                //交易状态
                String tradeStatus = params.containsKey("trade_status") ? params.get("trade_status") : "";

                String body = params.containsKey("body") ? params.get("body") : "";

                switch (tradeStatus) {
                    case "WAIT_BUYER_PAY":
//                        orderInfoService.changeStatus(orderId, tradeStatus);
                        break;
                    /*
                     * 关闭订单
                     * （1)订单已创建，但用户未付款，调用关闭交易接口
                     * （2）付款成功后，订单金额已全部退款【如果没有全部退完，仍是TRADE_SUCCESS状态】
                     */
                    case "TRADE_CLOSED":
//                        orderInfoService.changeStatus(orderId, tradeStatus);
                        break;
                    /*
                     * 订单完成
                     * （1）退款日期超过可退款期限后
                     */
                    case "TRADE_FINISHED":
//                        orderInfoService.changeStatus(orderId, tradeStatus);
                        break;
                    /*
                     * 订单Success
                     * （1）用户付款成功
                     */
                    case "TRADE_SUCCESS":


                        FeeRecord feeRecord = new FeeRecord();
                        if (orderId.indexOf("REGI") > -1) { //indexOf() 方法可返回某个指定的字符串值在字符串中首次出现的位置
                            //更新付款状态
                            Registered registered = new Registered();
                            registered.setRegisteredId(orderId);
                            registeredMapper.updateStatus(registered);

                            //添加挂号费用记录：姓名，付款类别，付款数，收款人，收款时间，支付宝账单，备注
                            Registered registeredRecord = registeredMapper.selectById(orderId);
                            feeRecord.setUserId(registeredRecord.getUserId());
                            feeRecord.setUserName(userMapper.selectById(registeredRecord.getUserId()).getUserName());
                            feeRecord.setFeeType("挂号费用");
                            feeRecord.setCreateUser(registeredRecord.getCreateUser());
                            feeRecord.setCreateUserName(userMapper.selectById(registeredRecord.getCreateUser()).getUserName());
                        } else {

                            CaseInfo caseInfo1 = caseInfoMapper.selectById(orderId);
                            caseInfo1.setCaseStatus("1");//诊断状态那里，已结束还是诊断中耶
                            caseInfoMapper.updateById(caseInfo1);

                            //添加药品费用记录:姓名，付款类别，付款数，收款人，收款时间，支付宝账单，备注
                            CaseInfo caseInfo = caseInfoMapper.selectById(orderId);
                            feeRecord.setUserId(caseInfo.getUserId());
                            feeRecord.setUserName(userMapper.selectById(caseInfo.getUserId()).getUserName());
                            feeRecord.setFeeType("药品费用");
                            feeRecord.setCreateUser(caseInfo.getDoctorId());
                            feeRecord.setCreateUserName(userMapper.selectById(caseInfo.getDoctorId()).getUserName());

                            QueryWrapper<CaseMedicine> wrapper = new QueryWrapper<>();
                            wrapper.lambda().eq(CaseMedicine::getCaseId, orderId);

                            List<CaseMedicine> caseMedicines = caseMedicineMapper.selectList(wrapper);
                            //到药品啦药品数量的变动啦啦
                            for (CaseMedicine caseMedicine : caseMedicines) {
                                Medicine medicine = medicineMapper.selectById(caseMedicine.getMedicineId());
                                medicine.setAmount(medicine.getAmount() - caseMedicine.getAmount());
                                medicineMapper.updateById(medicine);
                            }
                        }
                        feeRecord.setRecordId(IDGenerator.getUUID("FEERECORD"));
                        feeRecord.setFeeAmount(Double.parseDouble(totalAmount));
                        feeRecord.setCreateTime(new Date());
                        feeRecord.setTradeNo(tradeNo);
                        feeRecord.setOrderId(orderId);
                        feeRecord.setRemark(body);
                        feeRecordMapper.insert(feeRecord);
                        break;
                    default:
                        break;
                }
                response.getWriter().write("success");
            } else {
                log.error("【支付异步方法】验证失败，错误信息：{}", AlipaySignature.getSignCheckContentV1(params));
                response.getWriter().write("fail");
            }
        } catch (Exception e) {
            log.error("【支付异步方法】异常，异常信息：{}", e.getMessage());
            e.printStackTrace();
        }
    }


    private Map<String, String> getPayParams(HttpServletRequest request) {
        Map<String, String> params = new HashMap<>(16);
        Map<String, String[]> requestParams = request.getParameterMap();

        Iterator<String> iter = requestParams.keySet().iterator();
        while (iter.hasNext()) {
            String name = iter.next();
            String[] values = requestParams.get(name);
            String valueStr = "";
            for (int i = 0; i < values.length; i++) {
                valueStr = (i == values.length - 1) ? valueStr + values[i]
                        : valueStr + values[i] + ",";
            }
            //乱码解决，这段代码在出现乱码时使用
//            valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
            params.put(name, valueStr);
        }
        return params;
    }
}